﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using MahApps.Metro.Controls;
using Utils.Config;
using FrameworkTemplate.Windows.Commands;

namespace FrameworkTemplate.Windows
{
    /// <summary>
    /// Interaction logic for ConfigWindow.xaml
    /// </summary>
    public partial class ConfigWindow : MetroWindow
    {
        string begin_edit_value;
        bool editable = false;
        string config_path;
        Template config;
        List<dynamic> 表格组;
        List<KeyValuePair<dynamic, object>> 文本框;//TextBox
        List<KeyValuePair<dynamic, object>> 数字框;//NumericUpDown
        List<KeyValuePair<dynamic, object>> 下拉框;//ComboBox
        public ConfigWindow(Template config, bool editable, string config_path)
        {
            this.config = config;
            this.editable = editable;
            this.config_path = config_path;
            InitializeComponent();
            表格组 = new List<dynamic>();
            文本框 = new List<KeyValuePair<dynamic, object>>();//TextBox
            数字框 = new List<KeyValuePair<dynamic, object>>();//NumericUpDown
            下拉框 = new List<KeyValuePair<dynamic, object>>();//ComboBox

            //绑定菜单命令
            CommandBinding open_folder = new CommandBinding(ApplicationCommands.Open, OpenFolder);
            this.CommandBindings.Add(open_folder);
            CommandBinding add_line = new CommandBinding(ApplicationCommands.New, AddLine);
            add_line.CanExecute += Add_Line_CanExecute;
            this.CommandBindings.Add(add_line);
            CommandBinding remove_line = new CommandBinding(ApplicationCommands.Delete, RemoveLine);
            remove_line.CanExecute += Remove_Line_CanExecute;
            this.CommandBindings.Add(remove_line);
        }

        private void Add_Line_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            var control = e.Source as DataGrid;
            if (control == null)
                e.CanExecute = false;
            e.CanExecute = true;
        }

        private void Remove_Line_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            var control = e.Source as DataGrid;
            if (control == null)
                e.CanExecute = false;
            else if (control.SelectedIndex != -1)
                e.CanExecute = false;
            e.CanExecute = true;
        }

        private void RemoveLine(object sender, ExecutedRoutedEventArgs e)
        {
            var control = e.Source as DataGrid;
            if (control == null)
                return;
            var itemSource = control.ItemsSource as BindingList<KVP<string, string>>;
            if (control.SelectedIndex != -1)
                itemSource.RemoveAt(control.SelectedIndex);
            //control.Items.DeferRefresh();
        }

        private void AddLine(object sender, ExecutedRoutedEventArgs e)
        {
            var control = e.Source as DataGrid;
            if (control == null)
                return;
            var itemSource = control.ItemsSource as BindingList<KVP<string, string>>;
            foreach(var i in itemSource)
            {
                if (i.Key == "new_config_key")
                    return;
            }
            itemSource.Add(new KVP<string, string>("new_config_key", "new_config_value"));
        }

        private void OpenFolder(object sender, ExecutedRoutedEventArgs e)
        {
            dynamic control = e.Parameter;
            System.Windows.Forms.FolderBrowserDialog fbd = new System.Windows.Forms.FolderBrowserDialog
            {
                Description = System.Windows.Application.Current.FindResource("config_select_folder") as string
            };
            if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if (string.IsNullOrEmpty(fbd.SelectedPath))
                {
                    System.Windows.MessageBox.Show(this, System.Windows.Application.Current.FindResource("config_select_folder_msg") as string
                        , System.Windows.Application.Current.FindResource("config_select_folder_msg_title") as string);
                    return;
                }
                if(e.Parameter.GetType().GetRuntimeProperty("Text") != null)
                    control.Text = fbd.SelectedPath;
            }
        }

        /// <summary>
        /// 一个事件通知的键值对类s
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        class KVP<TKey, TValue> : INotifyPropertyChanged
        {
            TValue value;
            public TKey Key { get; set; }
            public TValue Value
            {
                get =>value;
                set
                {
                    this.value = value;
                    NotifyPropertyChanged();
                }
            }

            public KVP(TKey key, TValue value)
            {
                Key = key;
                Value = value;
            }

            public event PropertyChangedEventHandler PropertyChanged;
            // This method is called by the Set accessor of each property.
            // The CallerMemberName attribute that is applied to the optional propertyName
            // parameter causes the property name of the caller to be substituted as an argument.
            private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void BindControls()
        {
            //绑定字典到datagrid
            表格组.Add(new
            {
                datagrid = connections,//DataGrid控件
                dic = config.程序.连接地址,//绑定的字典对象
                kvp_list = new BindingList<KVP<string, string>>()//固定,新建带键值对的KVP数组
            });
            表格组.Add(new
            {
                datagrid = registers,
                dic = config.PLC.寄存器,
                kvp_list = new BindingList<KVP<string, string>>()
            });
            表格组.Add(new
            {
                datagrid = host_types,
                dic = config.脚本.引入类型,
                kvp_list = new BindingList<KVP<string, string>>()
            });

            //添加到绑定列表(文本框)
            //Key 为TextBox或其他有Name和Text属性的对象
            //Value 为包含该控件Name属性的配置项对象
            //以下为示例: 将名字为 日志压缩周期 的TextBox控件属性绑定到 config.程序.日志压缩周期
            文本框.Add(new KeyValuePair<dynamic, object>(日志文件夹, config.程序));
            文本框.Add(new KeyValuePair<dynamic, object>(历史数据文件夹, config.程序));
            文本框.Add(new KeyValuePair<dynamic, object>(文件夹, config.脚本));

            //绑定到数字框
            数字框.Add(new KeyValuePair<dynamic, object>(日志压缩周期, config.程序));

            //添加到绑定列表(下拉框)
            //绑定方法同文本框,
            下拉框.Add(new KeyValuePair<dynamic, object>(PLC_type, config.程序));
            下拉框.Add(new KeyValuePair<dynamic, object>(SQL_type, config.程序));


            //将所有下拉框用到的枚举设为下拉框的数据源
            foreach (var kvp in 下拉框)
            {
                kvp.Key.ItemsSource = kvp.Value.GetType()
                    .GetProperty(kvp.Key.Name)
                    .GetValue(kvp.Value)
                    .GetType()
                    .GetEnumValues();
            }

        }
        public void UpdateEditable(bool editable)
        {
            if(editable)
            {
                save.IsEnabled = true;
                reset.IsEnabled = true;
                connections.IsReadOnly = false;
                registers.IsReadOnly = false;
                host_types.IsReadOnly = false;
            }
            else
            {
                save.IsEnabled = false;
                reset.IsEnabled = false;
                connections.IsReadOnly = true;
                registers.IsReadOnly = true;
                host_types.IsReadOnly = true;

            }
        }

        private void Save()
        {
            //遍历取出键值对数组并更新到配置项的字典
            foreach (var item in 表格组)
            {
                item.dic.Clear();
                foreach (var kvp in item.kvp_list)
                {
                    item.dic[kvp.Key] = kvp.Value;
                }
            }
            //从文本框取值并更新到配置项
            foreach (var kvp in 文本框)
            {
                var k = kvp.Key;
                var v = kvp.Value;
                var p = v.GetType().GetProperty(k.Name);
                dynamic result = k.Text;
                var src = p.GetValue(v);
                try
                {
                    p.SetValue(v, result);
                }
                catch(Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
            //从数字框更新到配置项
            foreach (var kvp in 数字框)
            {
                var type = kvp.Value.GetType().GetProperty(kvp.Key.Name);
                var value = kvp.Key.Value;
                type.SetValue(kvp.Value, 
                    Convert.ChangeType(value, 
                        Convert.GetTypeCode(type.GetValue(kvp.Value))));
            }
            //从下拉框取值并更新到配置项
            foreach (var kvp in 下拉框)
            {
                kvp.Value.GetType()
                    .GetProperty(kvp.Key.Name)
                    .SetValue(kvp.Value, kvp.Key.SelectedItem);
            }

            ConfigUtil.Write(config_path, config);
        }

        private void Reset()
        {
            //从配置项更新到所有datagrid
            foreach (var item in 表格组)
            {
                item.kvp_list.Clear();
                var dic = (item.dic as Dictionary<string, string>).ToList();
                if(dic.Count != 0)
                    dic.ForEach(kvp => item.kvp_list.Add(new KVP<string, string>(kvp.Key, kvp.Value)));
                item.datagrid.ItemsSource = item.kvp_list;
                item.datagrid.Items.Refresh();
            }
            //从配置项更新到所有文本框
            foreach (var kvp in 文本框)
            {
                kvp.Key.Text = kvp.Value.GetType()
                    .GetProperty(kvp.Key.Name)
                    .GetValue(kvp.Value)
                    .ToString();
            }

            //从配置项更新到所有数字框
            foreach (var kvp in 数字框)
            {
                kvp.Key.Value = kvp.Value.GetType()
                    .GetProperty(kvp.Key.Name)
                    .GetValue(kvp.Value);
            }
            //从配置项更新到所有下拉框
            foreach (var kvp in 下拉框)
            {
                kvp.Key.SelectedItem = kvp.Value.GetType()
                    .GetProperty(kvp.Key.Name)
                    .GetValue(kvp.Value);
            }
        }

        private void Update()
        {

        }

        private void Cancel_Click(object sender, RoutedEventArgs e) => this.Close();

        private void Save_Click(object sender, RoutedEventArgs e) => Save();

        private void Reset_Click(object sender, RoutedEventArgs e) => Reset();

        private void Update_Click(object sender, RoutedEventArgs e) => Update();

        private void Connections_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            var grid = sender as DataGrid;
            var width = e.NewSize.Width - grid.Columns[0].MinWidth;
            if (double.IsNaN(width))
                return;
            grid.Columns[1].Width = width - 10;
        }
        private void MetroWindow_Initialized(object sender, EventArgs e)
        {
        }

        private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
        {

            BindControls();
            Reset();
            UpdateEditable(editable);
        }

        private void CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            if(e.Column.DisplayIndex == 0)
            {
                var grid = sender as DataGrid;
                var textbox = e.EditingElement as TextBox;
                var item = e.Row.Item as KVP<string, string>;
                if (item.Key == "")
                {
                    item.Key = begin_edit_value;
                    return;
                }
                var itemSource = grid.ItemsSource as BindingList<KVP<string, string>>;
                for(var i  = 0; i < itemSource.Count;i++)
                {
                    if(i != e.Row.GetIndex()&& itemSource[i].Key == item.Key)
                    {
                        item.Key = begin_edit_value;
                        break;
                    }
                }
            }

        }

        private void BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
        {
            if (e.Column.DisplayIndex == 0)
            {
                begin_edit_value = (e.Row.Item as KVP<string, string>).Key;
            }
        }
    }
}
